package cn.chiship.sdk.pay.core.util;

import cn.chiship.sdk.core.base.BaseResult;
import cn.chiship.sdk.core.util.PrintUtil;
import cn.chiship.sdk.core.util.StringUtil;
import cn.chiship.sdk.pay.core.config.AliPayConfig;
import cn.chiship.sdk.pay.core.model.PayBillDownloadModel;
import cn.chiship.sdk.pay.core.model.zfb.ZfbRefund;
import cn.chiship.sdk.pay.core.model.zfb.ZfbTransfer;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayResponse;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.diagnosis.DiagnosisUtils;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 支付宝支付工具
 *
 * @author lj
 */
public class AliPayUtil {

    private String signType = "RSA2";

    private String gateUrl = "https://openapi.alipay.com/gateway.do";

    private String charset = "UTF-8";

    private String format = "json";


    protected static final Logger LOGGER = LoggerFactory.getLogger(AliPayUtil.class);

    private static AliPayUtil aliPayUtil;

    private AliPayConfig aliPayConfig;

    private AlipayClient alipayClient;

    private static final Map<String, AlipayClient> alipayClientMap = new ConcurrentHashMap<>();


    private AliPayUtil() {
    }

    public static synchronized AliPayUtil getInstance() {
        if (aliPayUtil == null) {
            aliPayUtil = new AliPayUtil();
        }

        return aliPayUtil;
    }

    public AliPayUtil config(AliPayConfig aliPayConfig) {
        this.aliPayConfig = aliPayConfig;
        this.alipayClient = createClient();
        return this;
    }

    public BaseResult doPagePay(String orderId, Double totalAmount, String orderName, String orderDesc,
                                String aliPayReturnUrl, String aliPayNotifyUrl) {
        try {
            LOGGER.info(
                    "-----------------进入支付宝PC网站支付 详看文档：https://opendocs.alipay.com/open/59da99d0_alipay.trade.page.pay?scene=22&pathHash=e26b497f-----------------------");
            AlipayTradePagePayRequest aliPayRequest = new AlipayTradePagePayRequest();
            aliPayRequest.setReturnUrl(aliPayReturnUrl);
            aliPayRequest.setNotifyUrl(aliPayNotifyUrl);
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderId);
            bizContent.put("total_amount", totalAmount);
            bizContent.put("subject", orderName);
            bizContent.put("body", orderDesc);
            bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
            aliPayRequest.setBizContent(bizContent.toJSONString());
            AlipayResponse alipayResponse = alipayClient.pageExecute(aliPayRequest);
            if (alipayResponse.isSuccess()) {
                return BaseResult.ok(alipayResponse.getBody());
            } else {
                return BaseResult.error("支付失败！");
            }
        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 手机H5支付
     *
     * @param orderId         订单号
     * @param totalAmount     支付金额（元）
     * @param orderName       订单名称
     * @param orderDesc       订单描述
     * @param aliPayReturnUrl 返回结果
     * @param aliPayNotifyUrl 异步通知
     * @return {"success":true, "code":200, "message":"操作成功", "data":手机支付form表单}
     */
    public BaseResult doWapPay(String orderId, Double totalAmount, String orderName, String orderDesc,
                               String aliPayReturnUrl, String aliPayNotifyUrl) {
        try {
            LOGGER.info(
                    "-----------------进入支付宝手机网站支付 详看文档：https://opendocs.alipay.com/open/29ae8cb6_alipay.trade.wap.pay?scene=21&pathHash=1ef587fd-----------------------");
            AlipayTradeWapPayRequest aliPayRequest = new AlipayTradeWapPayRequest();
            AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
            model.setOutTradeNo(orderId);
            model.setSubject(orderName);
            model.setTotalAmount(String.valueOf(totalAmount));
            model.setBody(orderDesc);
            model.setTimeoutExpress("2m");
            model.setProductCode("QUICK_WAP_WAY");
            aliPayRequest.setBizModel(model);
            aliPayRequest.setReturnUrl(aliPayReturnUrl);
            aliPayRequest.setNotifyUrl(aliPayNotifyUrl);
            AlipayTradeWapPayResponse response = alipayClient.pageExecute(aliPayRequest);
            if (response.isSuccess()) {
                return BaseResult.ok(response.getBody());
            } else {
                return BaseResult.error("支付失败！");
            }

        } catch (AlipayApiException e) {
            LOGGER.error("发生异常", e);
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            LOGGER.error("发生异常", e);
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 支付宝扫码支付
     *
     * @param orderId         订单号
     * @param totalAmount     支付金额(元)
     * @param orderName       订单名称
     * @param orderDesc       订单描述
     * @param aliPayNotifyUrl 异步通知
     * @return 返回结果 Example：{"success":true, "code":200, "message":"操作成功",
     * "data":{qrCode=https://qr.alipay.com/bax06063blhwftyosa4l55e0}}
     */
    public BaseResult doQrCodePay(String orderId, Double totalAmount, String orderName, String orderDesc,
                                  String aliPayNotifyUrl) {
        try {
            LOGGER.info(
                    "-----------------进入支付宝当面付扫码支付 详看文档：https://opendocs.alipay.com/open/f540afd8_alipay.trade.precreate?scene=19&pathHash=d3c84596-----------------------");
            AlipayTradePrecreateRequest alipayTradePrecreateRequest = new AlipayTradePrecreateRequest();
            alipayTradePrecreateRequest.setNotifyUrl(aliPayNotifyUrl);
            JSONObject bizContent = new JSONObject();
            bizContent.put("out_trade_no", orderId);
            bizContent.put("total_amount", totalAmount);
            bizContent.put("subject", orderName);
            bizContent.put("body", orderDesc);

            alipayTradePrecreateRequest.setBizContent(bizContent.toString());
            AlipayTradePrecreateResponse alipayResponse = alipayClient.execute(alipayTradePrecreateRequest);
            if (alipayResponse.isSuccess()) {
                JSONObject json = JSON.parseObject(alipayResponse.getBody())
                        .getJSONObject("alipay_trade_precreate_response");
                return BaseResult.ok(json.getString("qr_code"));
            } else {
                return BaseResult.error(alipayResponse.getSubMsg());
            }
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 支付宝JSAPI支付
     *
     * @param openId
     * @param orderId
     * @param totalAmount
     * @param orderName
     * @param orderDesc
     * @param aliPayNotifyUrl
     * @return
     */
    public BaseResult doJsApiPay(String openId, String orderId, Double totalAmount, String orderName, String orderDesc,
                                 String aliPayNotifyUrl) {
        try {
            LOGGER.info(
                    "-----------------进入支付宝 JSAPI支付 详看文档：https://opendocs.alipay.com/mini/6039ed0c_alipay.trade.create?scene=de4d6a1e0c6e423b9eefa7c3a6dcb7a5&pathHash=779dc517-----------------------");
            AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
            request.setNotifyUrl(aliPayNotifyUrl);
            AlipayTradeCreateModel model = new AlipayTradeCreateModel();
            model.setOutTradeNo(orderId);
            model.setProductCode("JSAPI_PAY");
            model.setOpAppId(aliPayConfig.getAppId());
            model.setTotalAmount(String.valueOf(totalAmount));
            model.setSubject(orderName);
            model.setBody(orderDesc);
            model.setBuyerOpenId(openId);

            request.setBizModel(model);
            AlipayTradeCreateResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                JSONObject json = JSON.parseObject(response.getBody())
                        .getJSONObject("alipay_trade_create_response");
                return BaseResult.ok(json);
            } else {
                PrintUtil.console("换取授权访问令牌出错，诊断链接：" + DiagnosisUtils.getDiagnosisUrl(response));
                return BaseResult.error(response.getSubMsg());
            }
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 支付宝订单查询
     *
     * @param tradeNo 交易号
     * @param isOrder true 订单号查询 false 流水号查询
     * @return BaseResult
     */
    public BaseResult doQuery(String tradeNo, Boolean isOrder) {
        LOGGER.info(
                "-----------------进入支付宝订单查询 详看文档：https://opendocs.alipay.com/open/4e2d51d1_alipay.trade.query?scene=common&pathHash=8abc6ffe-----------------------");

        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        if (Boolean.TRUE.equals(isOrder)) {
            model.setOutTradeNo(tradeNo);
        } else {
            model.setTradeNo(tradeNo);
        }
        List<String> queryOptions = new ArrayList<>();
        queryOptions.add("trade_settle_info");
        model.setQueryOptions(queryOptions);
        request.setBizModel(model);
        try {
            AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.execute(request);
            if (!alipayTradeQueryResponse.isSuccess()) {
                return BaseResult.error(alipayTradeQueryResponse.getSubMsg());
            }
            JSONObject json = JSON.parseObject(alipayTradeQueryResponse.getBody());
            JSONObject response = json.getJSONObject("alipay_trade_query_response");
            return BaseResult.ok(response);
        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 下载账单
     *
     * @param payBillDownloadModel
     * @return BaseResult
     */
    public BaseResult downloadBill(PayBillDownloadModel payBillDownloadModel) {
        LOGGER.info(
                "-----------------进入支付宝下载账单 详看文档：https://opendocs.alipay.com/open/3c9f1bcf_alipay.data.dataservice.bill.downloadurl.query?scene=common&pathHash=97357e8b-----------------------");

        String billType = payBillDownloadModel.getBillType();
        String billDate = payBillDownloadModel.getBillDate();
        if (StringUtil.isNullOrEmpty(billType)) {
            billType = "trade";
        }
        AlipayDataDataserviceBillDownloadurlQueryRequest aliPayRequest = new AlipayDataDataserviceBillDownloadurlQueryRequest();
        AlipayDataDataserviceBillDownloadurlQueryModel model = new AlipayDataDataserviceBillDownloadurlQueryModel();
        model.setBillType(billType);
        model.setBillDate(billDate);
        aliPayRequest.setBizModel(model);

        try {
            AlipayDataDataserviceBillDownloadurlQueryResponse alipayDataDataserviceBillDownloadurlQueryResponse = alipayClient
                    .execute(aliPayRequest);
            JSONObject json = JSON.parseObject(alipayDataDataserviceBillDownloadurlQueryResponse.getBody());
            if (!alipayDataDataserviceBillDownloadurlQueryResponse.isSuccess()) {
                return BaseResult.error(alipayDataDataserviceBillDownloadurlQueryResponse.getSubMsg());
            }
            JSONObject response = json.getJSONObject("alipay_data_dataservice_bill_downloadurl_query_response");
            return BaseResult.ok(response.getString("bill_download_url"));
        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 退款申请
     *
     * @param zfbRefund
     * @return BaseResult
     */
    public BaseResult doRefund(ZfbRefund zfbRefund) {
        LOGGER.info(
                "-----------------进入支付宝退款申请 详看文档：https://opendocs.alipay.com/open/4b7cc5db_alipay.trade.refund?scene=common&pathHash=d98b006d-----------------------");

        AlipayTradeRefundRequest aLiPayRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        model.setOutRequestNo(zfbRefund.getRefundNo());
        model.setTradeNo(zfbRefund.getTransactionId());
        model.setRefundAmount(String.valueOf(zfbRefund.getRefundAmount()));
        model.setRefundReason(zfbRefund.getRefundReason());
        aLiPayRequest.setBizModel(model);

        try {
            AlipayTradeRefundResponse alipayTradeRefundResponse = alipayClient.execute(aLiPayRequest);
            if (!alipayTradeRefundResponse.isSuccess()) {
                return BaseResult.error(alipayTradeRefundResponse.getSubMsg());
            }
            JSONObject json = JSON.parseObject(alipayTradeRefundResponse.getBody());
            JSONObject response = json.getJSONObject("alipay_trade_refund_response");
            response.put("refundNo", zfbRefund.getRefundNo());
            return BaseResult.ok(response);
        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 退款查询
     *
     * @param refundId 商户退款单号
     * @param orderNo  商户订单号
     * @return BaseResult
     */
    public BaseResult doRefundQuery(String refundId, String orderNo) {
        LOGGER.info(
                "-----------------进入支付宝 退款查询 ,详看文档：https://opendocs.alipay.com/open/7be83133_alipay.trade.fastpay.refund.query?scene=common&pathHash=7cf4fed5-----------------------");

        try {
            AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
            AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
            model.setOutRequestNo(refundId);
            model.setOutTradeNo(orderNo);
            List<String> queryOptions = new ArrayList<>();
            queryOptions.add("refund_detail_item_list");
            model.setQueryOptions(queryOptions);
            request.setBizModel(model);
            AlipayTradeFastpayRefundQueryResponse alipayTradeFastpayRefundQueryResponse = alipayClient.execute(request);
            if (!alipayTradeFastpayRefundQueryResponse.isSuccess()) {
                return BaseResult.error(alipayTradeFastpayRefundQueryResponse.getSubMsg());
            }
            JSONObject json = JSON.parseObject(alipayTradeFastpayRefundQueryResponse.getBody());
            JSONObject response = json.getJSONObject("alipay_trade_fastpay_refund_query_response");
            response.put("refundNo", refundId);
            return BaseResult.ok(response);

        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 转账
     *
     * @param zfbTransfer 转账实体
     * @return BaseResult
     */
    public BaseResult doTransfer(ZfbTransfer zfbTransfer) {
        LOGGER.info("-----------------进入支付宝转账,详看文档：https://opendocs.alipay.com/open/029i78-----------------------");

        try {
            AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest();
            AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();

            model.setOutBizNo(zfbTransfer.getTransferNo());
            model.setTransAmount(String.valueOf(zfbTransfer.getTransferAmount()));
            model.setOrderTitle(zfbTransfer.getTransferName());
            model.setRemark(zfbTransfer.getTransferRemark());
            /**
             * 业务产品码， 单笔无密转账到支付宝账户固定为: TRANS_ACCOUNT_NO_PWD； 收发现金红包固定为: STD_RED_PACKET；
             */
            model.setProductCode("TRANS_ACCOUNT_NO_PWD");

            Participant payeeInfo = new Participant();
            payeeInfo.setIdentity(zfbTransfer.getUserId());
            /**
             * 参与方的标识类型，目前支持如下类型： 1、ALIPAY_USER_ID 支付宝的会员ID
             * 2、ALIPAY_LOGON_ID：支付宝登录号，支持邮箱和手机号格式 3、ALIPAY_OPEN_ID：支付宝openid
             */
            payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
            model.setPayeeInfo(payeeInfo);

            // 设置转账业务请求的扩展参数
            // model.setBusinessParams("{\"sub_biz_scene\":\"REDPACKET\",\"withdraw_timeliness\":\"T0\"}");

            request.setBizModel(model);
            AlipayFundTransUniTransferResponse alipayFundTransUniTransferResponse = alipayClient
                    .certificateExecute(request);
            if (!alipayFundTransUniTransferResponse.isSuccess()) {
                return BaseResult.error(alipayFundTransUniTransferResponse.getSubMsg());
            }
            JSONObject json = JSON.parseObject(alipayFundTransUniTransferResponse.getBody());
            JSONObject response = json.getJSONObject("alipay_fund_trans_uni_transfer_response");
            return BaseResult.ok(response);
        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    /**
     * 余额查询
     *
     * @return BaseResult
     */
    public BaseResult doBalanceQuery() {
        LOGGER.info(
                "-----------------进入支付宝商户余额查询,详看文档：https://opendocs.alipay.com/open/2acb3c34_alipay.data.bill.balance.query?scene=common&pathHash=e94f7ae2-----------------------");
        try {
            AlipayDataBillBalanceQueryRequest request = new AlipayDataBillBalanceQueryRequest();
            AlipayDataBillBalanceQueryResponse alipayDataBillBalanceQueryResponse = alipayClient
                    .certificateExecute(request);
            if (!alipayDataBillBalanceQueryResponse.isSuccess()) {
                return BaseResult.error(alipayDataBillBalanceQueryResponse.getSubMsg());
            }
            JSONObject json = JSON.parseObject(alipayDataBillBalanceQueryResponse.getBody());
            JSONObject response = json.getJSONObject("alipay_data_bill_balance_query_response");
            return BaseResult.ok(response);
        } catch (AlipayApiException e) {
            return BaseResult.error(e.getErrMsg());
        } catch (Exception e) {
            return BaseResult.error(e.getMessage());
        }
    }

    public boolean rsaCheckV1(Map<String, String> params) throws AlipayApiException {
        LOGGER.info("-----------------进入支付宝验签-----------------------");

        return AlipaySignature.rsaCheckV1(params, aliPayConfig.getAliPayPublicKey(), charset, signType);
    }

    /**
     * 创建支付客户端
     *
     * @return
     */
    private AlipayClient createClient() {
        AlipayClient alipayClient = alipayClientMap.get(aliPayConfig.getAppId());
        if (null == alipayClient) {
            alipayClient = new DefaultAlipayClient(gateUrl,
                    aliPayConfig.getAppId(),
                    aliPayConfig.getRsaPrivateKey(),
                    format,
                    charset,
                    aliPayConfig.getAliPayPublicKey(),
                    signType
            );
            alipayClientMap.put(aliPayConfig.getAppId(), alipayClient);
        }
        return alipayClient;

    }

}
